﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RadowCore.Models;
using RadowCore.Helpers;
using System.Collections;
using System.Windows.Forms;
namespace RadowCore.Algo
{
    class TriggeredRule
    {
        public Rule rule { set; get; }
        public List<PGNode> path { set; get; }
    }
    class Analyzer
    {
        // a multim map to store for each visited vertix, it's corresponding list of triggered rules
        Dictionary<String, List<TriggeredRule>> triggeredRules = new Dictionary<String, List<TriggeredRule>>();
        //a collection to store the visited verticies
        ArrayList visitedVerticies = new ArrayList();
        public List<TriggeredRule> analyze(PGNode rootNode, Policy policy, List<PGNode> previousNodes)
        {
            List<TriggeredRule> result = new List<TriggeredRule>();
            //add the root node to the visited vertices
            visitedVerticies.Add(rootNode);            
            //check if the vertix triggers some rule, by searching for it's originator in the rules originators
            
            var trru = policy.RuleList.Where(r => r.Source == rootNode.Orignator || r.Source == rootNode.Role);
            foreach (Rule triggeredRule in trru)
            {
               
                // if the vertix didn't trigger any rule yet
                if (!triggeredRules.ContainsKey(rootNode.Name))
                {
                   //intiate the triggered list for the current vertix and add the current triggered rule to it.
                    triggeredRules[rootNode.Name] = new List<TriggeredRule>();
                    TriggeredRule triggeredRuleEntry = new TriggeredRule { rule = triggeredRule, path = new List<PGNode>() };
                    triggeredRuleEntry.path.Add(rootNode);
                    triggeredRules[rootNode.Name].Add(triggeredRuleEntry);
                    
                }
                else// if the vertix already triggered some rules
                {
                    //add the current rule to it's triggered rule list
                    TriggeredRule triggeredRuleEntry = new TriggeredRule { rule = triggeredRule, path = new List<PGNode>() };
                    triggeredRuleEntry.path.Add(rootNode);
                    triggeredRules[rootNode.Name].Add(triggeredRuleEntry);
                }
            }
            //now check if the current vertix, causes a triggered rules from previous visited vertices to fire:
            //clone the list of previousNodes:
            List<PGNode> newPrev = Extensions.Clone<PGNode>(previousNodes);
            newPrev.Add(rootNode);
            //go through all the previous nodes, and foreach one, check the rule list to see if any of them is triggered:
            foreach (PGNode prevVertix in previousNodes)
            {
                if (triggeredRules.ContainsKey(prevVertix.Name))
                {
                    foreach (TriggeredRule rule in triggeredRules[prevVertix.Name])
                    {
                        //add the current vertix to the path of previous nodes
                        rule.path.Add(rootNode);
                        if (rule.rule.Destination == rootNode.Orignator || rule.rule.Destination == rootNode.Role)//if the current vetix causes the rule to trigger
                        {
                            if (!rule.rule.Exception.Evaluate(rule.path))//evaluate the exception and report a violatoin if false
                            {                                
                                TriggeredRule Violation = new TriggeredRule { path = Extensions.Clone(rule.path), rule = rule.rule };
                                result.Add(Violation);
                            }
                        }
                    }
                }
            }           
            foreach (PGEdge next in rootNode.Edges)
            {
                List<TriggeredRule> subRestult = analyze(next.TargetNode, policy, newPrev);
                foreach (TriggeredRule tr in subRestult)
                    result.Add(tr);
            }
            foreach(PGNode node in previousNodes)
            {
                if (triggeredRules.ContainsKey(node.Name))
                {
                    foreach (TriggeredRule tr in triggeredRules[node.Name])
                    {
                        tr.path.Remove(rootNode);
                        if (tr.path.Count == 0)
                            triggeredRules[node.Name].Remove(tr);
                    }
                    if (triggeredRules[node.Name].Count == 0)
                        visitedVerticies.Remove(node);
                }
                
            }
            return result;
        }
    }
}
